/*
 * thermometerdevice.cpp
 *
 *   Created on: 6.02.2011
 *       Author: Mike Shatohin (brunql)
 *
 *      Project: USBtinyThermometer
 *
 *  Copyright (c) 2011 Mike Shatohin, mikeshatohin [at] gmail.com
 *
 *  USBtinyThermometer is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation, either version 2 of the License, or
 *  (at your option) any later version.
 *
 *  USBtinyThermometer is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 */

#include "thermometerdevice.h"
#include <string.h> // memset(...)
#include <math.h> // round(...)


ThermometerDevice::ThermometerDevice(int verboseLevel)
{
    this->verboseLevel = verboseLevel;

    memset(buffer, 0, sizeof(buffer));

    usbDevHandle = NULL;

    usb_init();

    if(usbOpenDevice(&usbDevHandle, USBTINY_VENDOR_ID, NULL, USBTINY_DEVICE_ID, NULL) != 0){
        fprintf(stderr, "Could not find USB device \"%s\" with vid=0x%x pid=0x%x\n", USBTINY_DEVICE_NAME, USBTINY_VENDOR_ID, USBTINY_DEVICE_ID);
        exit(-1);
    }

    out("Device open success\n");
}

int ThermometerDevice::findSensors()
{
    out("Find temperature sensors...");

    buffer[BUFF_CMD_INDEX] = CMD_FIND_SENSORS;
    usbOut();


    out("Get number of sensors...");

    buffer[BUFF_CMD_INDEX] = CMD_NOP;
    usbIn();

    return (buffer[BUFF_SENSORS_NUMBER_INDEX] & 0xff);
}



void ThermometerDevice::startMeasurement()
{
    out("Start measurement...");

    buffer[BUFF_CMD_INDEX] = CMD_START_MEASUREMENT;
    usbOut();

    out("Sleep 1 second while measuring...\n");
    sleep(1);
}


double ThermometerDevice::getThemperature(int sensorIndex, bool roundFirstDec)
{
    out("Read temperature from ");

    if(sensorIndex == FIRST_SENSOR){
        out("FIRST_SENSOR...");
        buffer[BUFF_CMD_INDEX] = CMD_READ_TEMPERATURE_FIRST;

    }else if(sensorIndex == SECOND_SENSOR){
        out("SECOND_SENSOR...");
        buffer[BUFF_CMD_INDEX] = CMD_READ_TEMPERATURE_SECOND;

    }else{
        fprintf(stderr, "Read temperature FAIL: sensorIndex=%d;\n", sensorIndex);
        exit(-2);
    }

    usbOut();


    out("Request temperature...");

    usbIn();

    outBuffer();

    uint8_t temp_lo = buffer[1] & 0xff;
    uint8_t temp_hi = buffer[0] & 0xff;

    int16_t temp = (temp_hi << 8) | (temp_lo);

    double result = temp / 16.0; // low 4 bits of temp are fractional part

    if(roundFirstDec){
        result *= 10;
        result = round( result );
        result /= 10;
    }

    return result;
}


QString ThermometerDevice::getSensorId(int sensorIndex)
{
    out("Get sensor ID for ");

    if(sensorIndex == FIRST_SENSOR){
        out("FIRST_SENSOR...");
        buffer[BUFF_CMD_INDEX] = CMD_READ_SENSOR_ID_FIRST;

    }else if(sensorIndex == SECOND_SENSOR){
        out("SECOND_SENSOR...");
        buffer[BUFF_CMD_INDEX] = CMD_READ_SENSOR_ID_SECOND;

    }else{
        fprintf(stderr, "Read sensor ID FAIL: sensorIndex=%d;\n", sensorIndex);
        exit(-3);
    }

    usbOut();


    out("Read ID...");

    usbIn();

    outBuffer();

    QString result = "";
    for(int i=0; i < 8; i++){
        result += QString::number((buffer[i] & 0xff), 16);
    }

    return result;
}

void ThermometerDevice::close()
{
    usb_close(usbDevHandle);
}





void ThermometerDevice::usbOut()
{
    int result = usb_control_msg(usbDevHandle, USB_ENDPOINT_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, 0, 0, 0, buffer, sizeof(buffer), 5000);
    if(result == sizeof(buffer)){
        out("OK\n");
        usleep(DELAY_BETWEEN_USB_REQUESTS_us);
    }else{
        // Find sensors
        out("FAIL\n");
        outFail(result, buffer, usb_strerror());
        exit(-93);
    }
}

void ThermometerDevice::usbIn()
{
    int result = usb_control_msg(usbDevHandle, USB_ENDPOINT_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, 0, 0, 0, buffer, sizeof(buffer), 5000);
    if(result == sizeof(buffer)){
        out("OK\n");
        usleep(DELAY_BETWEEN_USB_REQUESTS_us);
    }else{
        out("FAIL\n");
        outFail(result, buffer, usb_strerror());
        exit(-94);
    }
}

void ThermometerDevice::out(const char *str)
{
    if(verboseLevel){
        printf("%s", str);
        fflush(stdout);
    }
}

void ThermometerDevice::outFail(const int usb_control_msg_res, const char buff[BUFF_SIZE], const char * usb_error_str)
{
    if(verboseLevel){
        printf("  usb_control_msg_res = %d; \n", usb_control_msg_res);
        printf("  usb_error_str = %s; \n", usb_error_str);
        printf("  buffer = ");
        for(int i=0; i<BUFF_SIZE; i++){
            printf("0x%02X ", buff[i] & 0xff);
        }
        printf("\n");
        printf("  result_last_operation = 0x%02X; \n", (buff[BUFF_LAST_RESULT] & 0xff));
        fflush(stdout);
    }
}

void ThermometerDevice::outBuffer()
{
    if(verboseLevel){
        printf("Buffer: ");
        for(unsigned i = 0; i<sizeof(buffer); i++){
            printf("0x%02x ", buffer[i] & 0xff);
        }
        printf("\n");
    }
}
